Skip to content

compose: apply feature security metadata + entrypoint chaining to services#103

Merged
bilby91 merged 2 commits into
mainfrom
fix/compose-feature-security-entrypoint
Jun 24, 2026
Merged

compose: apply feature security metadata + entrypoint chaining to services#103
bilby91 merged 2 commits into
mainfrom
fix/compose-feature-security-entrypoint

Conversation

@bilby91

@bilby91 bilby91 commented Jun 24, 2026

Copy link
Copy Markdown
Member

Problem

Dev Container Features declare security options (privileged, init, capAdd, securityOpt) and entrypoint scripts in their metadata. For image/Dockerfile devcontainers these become docker run flags; the reference devcontainers/cli also applies them to docker-compose services via its generated override compose file (generateFeaturesComposeOverrideContent).

Our compose path merged the metadata into ResolvedConfig but never carried it onto the service. So a feature like docker-in-docker silently failed on compose-source devcontainers:

  • the container came up unprivileged, and
  • the feature's docker-init.sh entrypoint never ran,

…so docker inside the container reported the daemon was down. This was hit in real use on the DAP devcontainer (prebuilt image carrying dind in its devcontainer.metadata label).

Fix

Security options

  • Extend compose.Override with Privileged/Init/CapAdd/SecurityOpt; build it once from cfg in createFreshCompose and apply on both backends — ApplyRunOverride (native, in-memory project mutate) and WriteRunOverride (shellout YAML).
  • serviceToRunSpec now forwards Privileged/SecurityOpt to RunSpec (Init/CapAdd were already mapped).

Entrypoint chaining

  • Aggregate the metadata entrypoint chain into ResolvedConfig.Entrypoints (base-image label entries first, then features; idempotent).
  • RenderEntrypointWrapper builds the /bin/sh -c '<each entrypoint>; exec "$@"' wrapper that runs feature entrypoints before the original entrypoint+command. Native path emits a single $; shellout path doubles to $$ because docker compose re-interpolates the written YAML.
  • Add ImageDetails.Entrypoint (+ docker InspectImage) so a non-null image ENTRYPOINT is preserved underneath the wrapper.

Inspect contract

  • ContainerDetails gains Privileged/CapAdd/SecurityOpt, read from HostConfig in docker InspectContainer (reused by the podman backend), so tests can verify what actually landed on the container. Applecontainer can't surface these (VM isolation) — left at zero values.

Testing

  • Unit: wrapper rendering (escaped vs native $), override apply + YAML emit, and metadata aggregation/idempotency.
  • Integration (TestComposeSource_FeatureSecurityOptionsApplied): brings up a compose devcontainer whose feature declares privileged/init/capAdd/securityOpt and an entrypoint, then asserts all of them land on the real container via inspect + that the entrypoint marker ran. Green on both native and shellout backends.
  • Verified end-to-end against the real DAP devcontainer: dockerd now starts.

Scope / follow-up

Image-source (non-compose) entrypoint chaining is intentionally not included — newRunSpec doesn't consume cfg.Entrypoints yet because the interaction with OverrideCommand/keep-alive is delicate. The reported case (compose) is fully covered. Tracked as a follow-up.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Feature entrypoints can now be chained and wrapped so they run before the original container entrypoint/command.
    • Compose run overrides can now set and preserve security settings (privileged/init, additional capabilities, security options).
  • Bug Fixes

    • Security metadata is merged additively with stable deduplication (no duplicate capabilities/options).
    • Security and entrypoint details are now more accurately propagated into runtime inspection/output.
  • Tests

    • Expanded unit and integration coverage for entrypoint chaining, security option application, compose backends (native/shellout), and metadata merge/idempotency.

…vices

Dev Container Features declare security options (privileged, init, capAdd,
securityOpt) and entrypoint scripts in their metadata. For image/Dockerfile
devcontainers these become `docker run` flags; the reference devcontainers/cli
also applies them to docker-compose services via its generated override compose
file. Our compose path merged the metadata into ResolvedConfig but never carried
it onto the service, so features like docker-in-docker silently failed on
compose-source devcontainers: the daemon came up unprivileged and its
docker-init.sh entrypoint never ran, so `docker` reported the daemon down.

Security options:
- Extend compose.Override with Privileged/Init/CapAdd/SecurityOpt; build it
  once from cfg in createFreshCompose and apply on both backends
  (ApplyRunOverride for native, WriteRunOverride YAML for shellout).
- serviceToRunSpec now forwards Privileged/SecurityOpt to RunSpec (Init/CapAdd
  were already mapped).

Entrypoint chaining:
- Aggregate the metadata entrypoint chain into ResolvedConfig.Entrypoints
  (base-image label entries first, then features; idempotent).
- RenderEntrypointWrapper builds the `/bin/sh -c '<each entrypoint>; exec "$@"'`
  wrapper that runs feature entrypoints before the original entrypoint+command.
  Native path emits a single `$`; shellout path doubles to `$$` because
  `docker compose` re-interpolates the written YAML.
- Add ImageDetails.Entrypoint (+ docker InspectImage) so a non-null image
  ENTRYPOINT is preserved underneath the wrapper.

Inspect contract: ContainerDetails gains Privileged/CapAdd/SecurityOpt (read
from HostConfig in docker InspectContainer, reused by podman) so the new
integration test can verify what landed on the real container. Applecontainer
can't surface these (VM isolation) — left at zero values.

Tested: unit coverage for the wrapper rendering, override apply/emit, and
metadata aggregation; integration test brings up a compose devcontainer whose
feature declares privileged/init/capAdd/securityOpt + an entrypoint and asserts
all of them land on the real container — green on both native and shellout
backends. Image-source (non-compose) entrypoint chaining is left as a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7494e8b0-c46f-42d1-b964-fe225fca4b92

📥 Commits

Reviewing files that changed from the base of the PR and between 258d46e and 828cf59.

📒 Files selected for processing (1)
  • up.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • up.go

📝 Walkthrough

Walkthrough

The PR adds ordered entrypoint chaining and security metadata propagation across compose metadata merging, override generation/application, runtime inspection, and integration coverage for native and shellout compose backends.

Changes

Compose security and entrypoint chaining

Layer / File(s) Summary
Entrypoint metadata chain
config/resolved.go, config/merge_metadata.go, config/merge_metadata_test.go, runtime/runtime.go, runtime/docker/inspect.go
ResolvedConfig gains ordered entrypoints, MergeMetadata fills them from non-empty layer values, and image inspection copies the image entrypoint into runtime image details. Tests cover ordered, skipped-empty, and idempotent merges.
Override schema and wrapper
compose/override.go, compose/override_test.go
Override gains security and entrypoint-chain fields, RenderEntrypointWrapper builds the shell wrapper, and YAML override emission unions capabilities and security options before writing them. Tests cover security emission and wrapper escaping.
Run override application
up.go, compose/apply_override.go, compose/apply_override_test.go, compose/orchestrator.go, compose/orchestrator_test.go
createFreshCompose builds a shared runOverride, both compose backends consume it, ApplyRunOverride sets security fields and wraps the service entrypoint, and serviceToRunSpec copies security fields into runtime.RunSpec. Tests cover the apply path and run spec propagation.
Runtime security inspection
runtime/runtime.go, runtime/docker/inspect.go, runtime/applecontainer/inspect_darwin_arm64.go, test/integration/compose_feature_security_test.go
ContainerDetails gains security fields, Docker inspection maps them from HostConfig, the Apple snapshot comment notes zero values, and the integration test asserts security metadata and entrypoint chaining on native and shellout backends.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • crunchloop/devcontainer#21: Adds ordered Entrypoints accumulation in MergeMetadata, which matches the entrypoint chaining reducer introduced here.
  • crunchloop/devcontainer#64: Also updates compose/orchestrator.go to carry security-related container settings into the runtime run spec.

Poem

A bunny hopped through YAML haze,
With entrypoints in tidy ways.
exec "$@" sang soft and true,
While cap-add carrots leapt anew.
Native and shellout both took flight,
With security shining bright. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: applying feature security metadata and entrypoint chaining to compose services.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/compose-feature-security-entrypoint

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@up.go`:
- Around line 567-574: The InspectImage fallback in the entrypoint selection
logic is swallowing errors, so failed image inspection can silently leave
origEntrypoint unset while wrapper entrypoints are still applied. Update the
Entrypoints handling in the code around e.runtime.InspectImage to log the
inspect error when the service has no Entrypoint and finalImage inspection
fails, using the existing context from cfg.Entrypoints/src.Service so the
fallback behavior is observable and easier to diagnose.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78f9500f-ab79-4ecf-b96c-e6776318a803

📥 Commits

Reviewing files that changed from the base of the PR and between dcde75c and 258d46e.

📒 Files selected for processing (14)
  • compose/apply_override.go
  • compose/apply_override_test.go
  • compose/orchestrator.go
  • compose/orchestrator_test.go
  • compose/override.go
  • compose/override_test.go
  • config/merge_metadata.go
  • config/merge_metadata_test.go
  • config/resolved.go
  • runtime/applecontainer/inspect_darwin_arm64.go
  • runtime/docker/inspect.go
  • runtime/runtime.go
  • test/integration/compose_feature_security_test.go
  • up.go

Comment thread up.go
When a feature declares an entrypoint and the compose service declares
none, we inspect the final image to preserve its ENTRYPOINT under the
wrapper. A failed inspect left origEntrypoint nil and silently dropped
the image ENTRYPOINT from `exec "$@"`. Emit a WarnEvent so the fallback
is observable. Addresses CodeRabbit review on #103.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bilby91 bilby91 merged commit d191908 into main Jun 24, 2026
13 checks passed
bilby91 added a commit that referenced this pull request Jun 24, 2026
Document everything merged since v0.3.0: Podman backend +
checkpoint/restore (#98), compose feature security metadata + entrypoint
chaining (#103), Podman compose health probing (#102), deps bump (#101),
and the prebuild dev-environment / CI tooling (#88#93).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant